package com.zhaoziqian.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zhaoziqian.client.ChannelClient;
import com.zhaoziqian.client.FabricClient;
import com.zhaoziqian.config.EnvParam;
import com.zhaoziqian.model.*;
import com.zhaoziqian.service.Network;
import com.zhaoziqian.user.UserContext;
import com.zhaoziqian.utils.ReadNetworkConfig;
import com.zhaoziqian.utils.Util;
import org.apache.commons.lang3.StringUtils;
import org.hyperledger.fabric.sdk.*;
import org.hyperledger.fabric.sdk.exception.CryptoException;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.exception.TransactionException;
import org.hyperledger.fabric.sdk.helper.Utils;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.logging.Level;

import static com.zhaoziqian.config.Config.*;

/**
 * Created with fabric-cli
 * 作者： zhaoziqian
 * 时间： 2018/5/28  下午10:56
 * 功能：
 */
@Service
public class NetworkImpl implements Network {

    private Logger logger = LoggerFactory.getLogger(NetworkImpl.class);

    @Override
    public Channel createChannel(String ordererId, String ordererOrg, String org, String channelName) throws Exception {

        UserContext user = getIdentity(org);
        FabricClient fabClient = new FabricClient(user);
        // get orderer info
        OrdererModel ordererModel = ReadNetworkConfig.getOrderer(ordererOrg, ordererId);
        Orderer orderer = Util.createOrderer(fabClient,ordererModel);
        // create a new channel
        ChannelConfiguration channelConfiguration = new ChannelConfiguration(new File(CHANNEL_CONFIG_PATH));

        byte[] channelConfigurationSignatures = fabClient.getInstance().getChannelConfigurationSignature(channelConfiguration, user);

        Channel channel = fabClient.getInstance().newChannel(channelName, orderer, channelConfiguration, channelConfigurationSignatures);

        if (logger.isDebugEnabled()) {
            logger.debug("-----channel Deploy Wait Time : {} ------", channel.getDeployWaitTime());
            logger.debug("-----channel Transaction Wait Time : {} ------", channel.getTransactionWaitTime());
            logger.debug("-----channel isShutdown : {} ------", channel.isShutdown());
            logger.debug("-----channel name : {} ------", channel.getName());
            logger.debug("ORDERERS  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
            Collection<Orderer> orderers = channel.getOrderers();
            for (Orderer o : orderers) {
                logger.debug("----- orderer name : {} ------", o.getName());
                logger.debug("----- orderer Url : {} ------", o.getUrl());
            }
            logger.debug("ORDERERS  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
        }
        return channel;
    }

    @Override
    public Channel joinChannel(String ordererOrg, String ordererId, String peerId, String org, String channelName) throws Exception {

        UserContext user = getIdentity(org);
        FabricClient fabClient = new FabricClient(user);

        Channel channel = fabClient.getInstance().newChannel(channelName);

        OrdererModel ordererModel = ReadNetworkConfig.getOrderer(ordererOrg, ordererId);
        Orderer orderer = Util.createOrderer(fabClient,ordererModel);
        PeerModel peerModel = ReadNetworkConfig.getPeer(org, peerId);
        Peer peer = Util.createPeer(fabClient,peerModel);
        channel.addOrderer(orderer);
        channel.initialize();
        channel.joinPeer(peer);

        if (logger.isDebugEnabled()) {
            logger.debug("PEERS  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
            Collection peers = channel.getPeers();
            Iterator peerIter = peers.iterator();
            while (peerIter.hasNext()) {
                Peer pr = (Peer) peerIter.next();
                logger.debug("channel [ {} ] has peer [ {} ] at [ {} ] ", channel.getName(), pr.getName(), pr.getUrl());
            }
            logger.debug("PEERS  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
        }
        return channel;
    }

    @Override
    public SDKResponse installChainCode(String ordererOrg, String ordererId, String peerId, String org, String channelName, String chainCodePath, String chaincodeVersion, String chaincodeId) throws Exception {

        UserContext user = getIdentity(org);
        FabricClient fabClient = new FabricClient(user);

        Channel channel = fabClient.getInstance().newChannel(channelName);

        OrdererModel ordererModel = ReadNetworkConfig.getOrderer(ordererOrg, ordererId);
        Orderer orderer = Util.createOrderer(fabClient,ordererModel);
        PeerModel peerModel = ReadNetworkConfig.getPeer(org, peerId);
        Peer peer = Util.createPeer(fabClient,peerModel);
        channel.addOrderer(orderer);
        channel.addPeer(peer);
        channel.initialize();

        List<Peer> peers = new ArrayList<>();
        peers.add(peer);

        Collection<ProposalResponse> responses = fabClient.deployChainCode(chaincodeId, chainCodePath, EnvParam.CHAINCODE_ROOT_DIR,
                TransactionRequest.Type.GO_LANG.toString(), chaincodeVersion, peers);


        List<Map> responseList = new ArrayList<>();
        for (ProposalResponse res : responses) {
            logger.info("The result of installing a 【{} : {}】 on 【{}】 is 【{}】.", chainCodePath, chaincodeId, res.getPeer().getName(), res.getStatus().getStatus());
            Map item = new HashMap();

            item.put("peerName", res.getPeer().getName());
            item.put("peerUrl", res.getPeer().getUrl());
            item.put("respones_code", res.getStatus().getStatus());
            item.put("messages", res.getMessage());

            logger.debug("------ Respones Peer Name {} -------", res.getPeer().getName());
            logger.debug("------ Respones Peer Url {} -------", res.getPeer().getUrl());
            logger.debug("------ Respones Messages {} -------", res.getMessage());
            logger.debug("------ Respones TransactionID {} -------", res.getTransactionID());

            responseList.add(item);
        }

        return SDKResponse.OK(JSON.toJSONString(responseList));
    }

    @Override
    public SDKResponse instantiateChainCode(String ordererOrg, String ordererId, String peerId, String org, String channelName, String chainCodePath, String chaincodeVersion, String chaincodeId, String[] args) throws Exception {

        UserContext user = getIdentity(org);
        FabricClient fabClient = new FabricClient(user);

        Channel channel = fabClient.getInstance().newChannel(channelName);

        OrdererModel ordererModel = ReadNetworkConfig.getOrderer(ordererOrg, ordererId);
        Orderer orderer = Util.createOrderer(fabClient,ordererModel);
        PeerModel peerModel = ReadNetworkConfig.getPeer(org, peerId);
        Peer peer = Util.createPeer(fabClient,peerModel);
        channel.addOrderer(orderer);
        channel.addPeer(peer);
        channel.initialize();

        ChannelClient channelClient = new ChannelClient(channelName, channel, fabClient);

        String[] arguments = args;

        Collection<ProposalResponse> responses = channelClient.instantiateChainCode(chaincodeId, chaincodeVersion, chainCodePath, TransactionRequest.Type.GO_LANG.toString(), FUNCTION_INIT, arguments, null);

        List<Map> responseList = new ArrayList<>();
        for (ProposalResponse res : responses) {
            logger.info("The result of instantiating a 【{} : {}】 on 【{}】 is 【{}】.", chainCodePath, chaincodeId, res.getPeer().getName(), res.getStatus().getStatus());
            Map item = new HashMap();

            item.put("peerName", res.getPeer().getName());
            item.put("peerUrl", res.getPeer().getUrl());
            item.put("respones_code", res.getStatus().getStatus());
            item.put("messages", res.getMessage());

            logger.debug("------ Respones Peer Name {} -------", res.getPeer().getName());
            logger.debug("------ Respones Peer Url {} -------", res.getPeer().getUrl());
            logger.debug("------ Respones Messages {} -------", res.getMessage());
            logger.debug("------ Respones TransactionID {} -------", res.getTransactionID());

            responseList.add(item);
        }

        return SDKResponse.OK(JSON.toJSONString(responseList));
    }

    private UserContext getIdentity(String org) throws Exception {

        CryptoSuite.Factory.getCryptoSuite();
        Util.cleanUp();
        // get org admin info
        AdminModel adminModel = ReadNetworkConfig.getAdmin(NOT_ORDERER, org);
        // Construct Channel
        UserContext user = new UserContext();
        // load pk/ca file
        File pkFolder = new File(adminModel.getKeyPath());
        File[] pkFiles = pkFolder.listFiles();
        File certFolder = new File(adminModel.getCertPath());
        File[] certFiles = certFolder.listFiles();
        Enrollment enrollAdmin = Util.getEnrollment(adminModel.getKeyPath(), pkFiles[0].getName(),
                adminModel.getCertPath(), certFiles[0].getName());
        user.setEnrollment(enrollAdmin);
        user.setMspId(ReadNetworkConfig.getParam(NETWORK_CONFIG, PEER, org, MSPID));
        user.setName(ADMIN);

        return user;
    }
}
